package com.ytjj.qmyx.admin.service.data.impl;

import com.ytjj.common.constants.StatisticsConstants;
import com.ytjj.qmyx.admin.constants.data.MongoCollectionNameConstants;
import com.ytjj.common.constants.PlatformConstant;
import com.ytjj.qmyx.admin.dao.AnalysisDao;
import com.ytjj.qmyx.admin.dao.data.DataStatisticDao;
import com.ytjj.qmyx.admin.dao.data.PlatformStatisticDao;
import com.ytjj.qmyx.admin.eunm.ActivityEnums;
import com.ytjj.qmyx.admin.eunm.DataModelEnums;
import com.ytjj.qmyx.admin.model.request.dataStatistics.DataStatisticsRequest;
import com.ytjj.qmyx.admin.model.response.DataResponse;
import com.ytjj.qmyx.admin.model.response.DataStatisticsResponse;
import com.ytjj.qmyx.admin.service.data.DataService;
import com.ytjj.qmyx.mall.model.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 *  *
 **/
@Slf4j
@Service
public class DataServiceImpl implements DataService {

    @Resource
    private PlatformStatisticDao platformStatisticDao;

    @Resource
    private DataStatisticDao dataStatisticDao;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private AnalysisDao analysisDao;


    @Override
    public DataStatisticsResponse getData(DataStatisticsRequest dataStatisticsRequest) {
        DataStatisticsResponse dataStatisticsResponse = new DataStatisticsResponse();
        List<DataResponse> channelData = analysisDao.findDataInfo(dataStatisticsRequest.getPlatform(), dataStatisticsRequest.getChannel(), dataStatisticsRequest.getSearchDate());
        List<DataResponse> moduleChannelData = analysisDao.findModuleDataList(dataStatisticsRequest.getSearchDate(), dataStatisticsRequest.getChannel());
        dataStatisticsResponse.setAllChannelDataList(channelData);
        dataStatisticsResponse.setModuleChannelDataList(moduleChannelData);
        return dataStatisticsResponse;
    }


    /**
     * 统计分析 定时任务
     * @param localDate  == null 时统计昨天整天的数据
     */
    @Override
    public void timing(LocalDate localDate) {
        boolean timingDay = false;
        if(null == localDate) {
            localDate = LocalDate.now().minusDays(1);
            timingDay = true;
        }
        log.info("----开始数据分析----------\n{}", localDate);

        //获取需要统计的渠道
        Map channelMap = redisTemplate.opsForHash().entries(StatisticsConstants.SKIP_NUM + localDate);
        if (channelMap == null || channelMap.isEmpty()) return;

        analysisDao.delData(localDate);
        analysisDao.delDataModule(localDate);

        //分渠道统计
        for (Object channelName : channelMap.keySet()) {
            String channel = channelName.toString();
            if ("all".equals(channel)) continue;
            String platform = analysisDao.findPlatform(channel);
            if (StringUtils.isEmpty(platform)) continue;
            DataStatisticsRequest request = new DataStatisticsRequest(platform, channel, localDate.toString());
            saveResponse(request);
        }
        //各平台聚合统计
        DataStatisticsRequest request = new DataStatisticsRequest("h5", null, localDate.toString());
        saveResponse(request);
        DataStatisticsRequest request1 = new DataStatisticsRequest("android", null, localDate.toString());
        saveResponse(request1);
        DataStatisticsRequest request2 = new DataStatisticsRequest("ios", null, localDate.toString());
        saveResponse(request2);
        DataStatisticsRequest request3 = new DataStatisticsRequest("weapp", null, localDate.toString());
        saveResponse(request3);

        if(timingDay) redisTemplate.delete(StatisticsConstants.SKIP_NUM + localDate);

        log.info("----结束数据分析----------\n{}", localDate);
    }

    @Override
    public List<Channel> getChannel(String searchDate, String platform) {
        List<Channel> list = analysisDao.findChannel(searchDate,platform);
        return list;
    }

    private void saveResponse(DataStatisticsRequest request) {
        this.getSearchDate(request);
        DataStatisticsResponse response = getResponse(request);
        analysisDao.saveDataList(response.getAllChannelDataList());
        analysisDao.saveDataModuleList(response.getModuleChannelDataList());
    }

    /**
     * 获取统计结果
     *
     * @param dataStatisticsRequest
     * @return
     */
    private DataStatisticsResponse getResponse(DataStatisticsRequest dataStatisticsRequest) {
        DataStatisticsResponse dataStatisticsResponse = new DataStatisticsResponse();
        List<DataResponse> channelData = this.getChannelData(dataStatisticsRequest);
        ArrayList<DataResponse> moduleChannelData = this.getModuleChannelData(dataStatisticsRequest);
        dataStatisticsResponse.setAllChannelDataList(channelData);
        dataStatisticsResponse.setModuleChannelDataList(moduleChannelData);
        return dataStatisticsResponse;
    }


    /**
     * 获取汇总渠道数据
     *
     * @param request
     * @return
     */
    public List<DataResponse> getChannelData(DataStatisticsRequest request) {
        List<DataResponse> channelOrderDataList = new ArrayList<>();
        if (StringUtils.isNotEmpty(request.getChannel())) {
            List<String> channelList = this.getChannelList(request.getChannel());
            List<DataResponse> list = channelList.stream().map(channelItem -> this.channelDataNew(request, request.getSearchDate(), request.getPlatform(), channelItem)).collect(Collectors.toList());
            channelOrderDataList.addAll(list);
        } else {
            DataResponse channelOrderData = this.channelDataNew(request, request.getSearchDate(), request.getPlatform(), request.getChannel());
            channelOrderDataList.add(channelOrderData);
        }
        return channelOrderDataList;
    }


    /**
     * 获取模块渠道信息
     *
     * @param request
     * @return
     */
    public ArrayList<DataResponse> getModuleChannelData(DataStatisticsRequest request) {
        ArrayList<DataResponse> moduleDataList = new ArrayList<>();
        // 模块订单信息
        ActivityEnums[] activityModel = ActivityEnums.values();

        Arrays.asList(activityModel).stream().forEach(item -> {
            if (StringUtils.isNotEmpty(request.getChannel())) {
                List<String> channelList = this.getChannelList(request.getChannel());
                List<DataResponse> list = channelList.parallelStream().map(channelItem -> this.moduleChannelDataNew(request.getSearchDate(), request.getPlatform(), channelItem, item.getActivityCode())).collect(Collectors.toList());
                moduleDataList.addAll(list);
            } else {
                DataResponse channelOrderData = this.moduleChannelDataNew(request.getSearchDate(), request.getPlatform(), request.getChannel(), item.getActivityCode());
                moduleDataList.add(channelOrderData);
            }
        });
        return moduleDataList;
    }

    /**
     * 统计-数据分析统计
     * 汇总分析
     *
     * @param searchDate 统计日期
     * @param platform   平台
     * @param channel    渠道
     * @return
     */
    public DataResponse channelDataNew(DataStatisticsRequest request, String searchDate, String platform, String channel) {
        // 意向订单数
        Integer intentionOrderNum = getOrderNumber(searchDate, platform, channel, null, null, null, null);
        // 无地址订单数
        Integer noAddressOrderNum = getOrderNumber(searchDate, platform, channel, 0, null, null, null);
        // 支付订单数
        Integer payOrderNum = getOrderNumber(searchDate, platform, channel, null, 1, null, null);
        // 实际订单数
        Integer realOrderNum = intentionOrderNum - noAddressOrderNum;
        // 转发数
        Integer transPondNum = getTransPondNum(platform, channel, searchDate);

        DataResponse dateRes = getDateRes(searchDate, platform, channel, null, intentionOrderNum, noAddressOrderNum, payOrderNum, realOrderNum);

        // 激活用户
        Integer activeUser = platformStatisticDao.getActiveUser(request);

        // 新增用户
        Integer newAddUser = platformStatisticDao.getNewAddUser(request);

        // 首单人数
        Integer isFirstBuy = platformStatisticDao.getChannelOrderData(request.getStartTime(), request.getEndTime(), platform, channel, null, 1, 1,null,null);

        // 支付总金额
        BigDecimal payMoney = Optional.ofNullable(platformStatisticDao.getPayMoney(request)).orElse(BigDecimal.ZERO);
        // 成本
        BigDecimal costMoney = Optional.ofNullable(platformStatisticDao.getCostMoney(request)).orElse(BigDecimal.ZERO);
        // 利润
        BigDecimal profit = payMoney.subtract(costMoney);

        // 意向订单数Uv
        Integer intentionOrderNumUv = getOrderUvNumber(searchDate, platform, channel, null, null, null, null);
        // 无地址订单数UV
        Integer noAddressOrderNumUv = getOrderUvNumber(searchDate, platform, channel, 0, null, null, null);
        // 支付订单数UV
        Integer payOrderNumUv= getOrderUvNumber(searchDate, platform, channel, null, 1, null, null);

        setChannelDateRes(dateRes, activeUser, newAddUser, isFirstBuy, payMoney, costMoney, profit, intentionOrderNumUv,noAddressOrderNumUv,transPondNum,payOrderNumUv);
        return dateRes;
    }

    public void setChannelDateRes(DataResponse dateRes, Integer activeUser, Integer newAddUser, Integer isFirstBuy,
                                  BigDecimal payMoney, BigDecimal costMoney, BigDecimal profit, Integer intentionOrderNumUv,
                                  Integer noAddressOrderNumUv, Integer transPondNum, Integer payOrderNumUv) {
        dateRes.setActiveUser(activeUser);
        dateRes.setNewAddUser(newAddUser);
        dateRes.setIsFirstBuy(isFirstBuy);
        dateRes.setPayMoney(payMoney);
        dateRes.setCostMoney(costMoney);
        dateRes.setProfit(profit);
        dateRes.setIntentionOrderNumUv(intentionOrderNumUv);
        dateRes.setNoAddressOrderNumUv(noAddressOrderNumUv);
        dateRes.setTransPondNum(transPondNum);
        dateRes.setPayOrderNumUv(payOrderNumUv);
    }

    /**
     * 统计-数据分析统计
     * 模块分析
     *
     * @param searchDate 统计日期
     * @param platform   平台
     * @param channel    渠道
     * @return
     */
    public DataResponse moduleChannelDataNew(String searchDate, String platform, String channel, String activityCode) {
        // 意向订单数
        Integer intentionOrderNum = getOrderNumber(searchDate, platform, channel, null, null, activityCode, null);
        // 无地址订单数
        Integer noAddressOrderNum = getOrderNumber(searchDate, platform, channel, 0, null, activityCode, null);
        // 支付订单数
        Integer payOrderNum = getOrderNumber(searchDate, platform, channel, null, 1, activityCode, null);
        // 实际订单数
        Integer realOrderNum = intentionOrderNum - noAddressOrderNum;

        String pathCode = DataModelEnums.getPathCode(activityCode);

        DataResponse dateRes = getDateRes(searchDate, platform, channel, pathCode, intentionOrderNum, noAddressOrderNum, payOrderNum, realOrderNum);
        dateRes.setActivityType(ActivityEnums.getActivityType(activityCode));
        return dateRes;
    }

    /**
     * 获取数据分析实体
     *
     * @param searchDate
     * @param platform
     * @param channel
     * @param pathCode
     * @param intentionOrderNum
     * @param noAddressOrderNum
     * @param payOrderNum
     * @param realOrderNum
     * @return
     */
    public DataResponse getDateRes(String searchDate, String platform, String channel, String pathCode,
                                   Integer intentionOrderNum, Integer noAddressOrderNum, Integer payOrderNum, Integer realOrderNum) {
        Integer uv = this.getUvNew(searchDate, platform, channel, pathCode);
        // 订单率 实际订单数/uv
        BigDecimal orderRatio = uv == 0 ? BigDecimal.ZERO : new BigDecimal(realOrderNum).divide(BigDecimal.valueOf(uv), 4, RoundingMode.HALF_UP);
        // 支付率 （支付订单/实际订单数）
        BigDecimal payRatio = realOrderNum == 0 ? BigDecimal.ZERO : new BigDecimal(payOrderNum).divide(BigDecimal.valueOf(realOrderNum), 4, RoundingMode.HALF_UP);

        DataResponse dataResponse = new DataResponse();
        dataResponse.setChannel(channel);
        dataResponse.setPlatform(platform);
        dataResponse.setUv(uv);
        dataResponse.setIntentionOrderNum(intentionOrderNum);
        dataResponse.setNoAddressOrderNum(noAddressOrderNum);
        dataResponse.setPayOrderNum(payOrderNum);
        dataResponse.setRecordTime(new Date());
        dataResponse.setOrderRatio(orderRatio);
        dataResponse.setPayRatio(payRatio);
        dataResponse.setRealOrderNum(realOrderNum);
        dataResponse.setTime(searchDate);
        return dataResponse;
    }

    /**
     * 获取转发数
     *
     * @param platform
     * @param channel
     * @param searchDate
     * @return
     */
    public Integer getTransPondNum(String platform, String channel, String searchDate) {
        if (PlatformConstant.PLATFORM_H5.equals(platform) || StringUtils.isNotEmpty(channel)) {
            try {
                Boolean all = redisTemplate.opsForHash().hasKey(StatisticsConstants.SKIP_NUM + searchDate, "all");
                if (all) {
                    if (StringUtils.isNotEmpty(channel)) {
                        Object o = redisTemplate.opsForHash().get(StatisticsConstants.SKIP_NUM + searchDate, channel);
                        return o == null ? 0 : Integer.parseInt(o.toString());
                    } else {
                        Object o = redisTemplate.opsForHash().get(StatisticsConstants.SKIP_NUM + searchDate, "all");
                        return o == null ? 0 : Integer.parseInt(o.toString());
                    }
                } else {
                    Criteria criteria = Criteria.where("channel").is(channel);
                    return (int) mongoTemplate.count(Query.query(criteria), MongoCollectionNameConstants.SKIP + searchDate);
                }
            } catch (Exception e) {
            }
        }
        return 0;
    }


    /**
     * 查询订单数据
     *
     * @param searchDate
     * @param platform
     * @param channel
     * @param isAddress
     * @param isPay
     * @param activityType
     * @param isFirstBuy
     * @return
     */
    public int getOrderNumber(String searchDate, String platform, String channel, Integer isAddress, Integer isPay, String activityType, Integer isFirstBuy) {
        Criteria criteria = Criteria.where("platform").is(platform);
        if (StringUtils.isNotEmpty(channel)) criteria.and("channel").is(channel);
        if (StringUtils.isNotEmpty(activityType)) criteria.and("activityType").is(activityType);
        if (null != isAddress) criteria.and("isAddress").is(isAddress);
        if (null != isPay) criteria.and("isPay").is(isPay);
        if (null != isFirstBuy) criteria.and("isFirstBuy").is(isFirstBuy);
        long count = mongoTemplate.count(Query.query(criteria), MongoCollectionNameConstants.ORDER + searchDate);
        return (int) count;
    }

    /**
     * 查询订单数据
     *
     * @param searchDate
     * @param platform
     * @param channel
     * @param isAddress
     * @param isPay
     * @param activityType
     * @param isFirstBuy
     * @return
     */
    public int getOrderUvNumber(String searchDate, String platform, String channel, Integer isAddress, Integer isPay, String activityType, Integer isFirstBuy) {
        Criteria criteria = Criteria.where("platform").is(platform);
        if (StringUtils.isNotEmpty(channel)) criteria.and("channel").is(channel);
        if (StringUtils.isNotEmpty(activityType)) criteria.and("activityType").is(activityType);
        if (null != isAddress) criteria.and("isAddress").is(isAddress);
        if (null != isPay) criteria.and("isPay").is(isPay);
        if (null != isFirstBuy) criteria.and("isFirstBuy").is(isFirstBuy);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("uuid").count().as("count").last("dayTime").as("dayTime"),
                Aggregation.group("dayTime").count().as("count")
        );
        AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, MongoCollectionNameConstants.ORDER + searchDate, Map.class);
        List<Map> maps = results.getMappedResults();
        if (maps.size() == 0) return 0;
        Object count = maps.get(0).get("count");
        return Integer.parseInt(count.toString());
    }

    /**
     * 获取页面统计uv
     *
     * @param searchDate
     * @param channel
     * @return
     */
    public Integer getUvNew(String searchDate, String platform, String channel, String pathCode) {
        Criteria criteria = commonCriteria(platform, channel, searchDate, pathCode);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("uuid").count().as("count").last("dayTime").as("dayTime"),
                Aggregation.group("dayTime").count().as("count")
        );
        AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, MongoCollectionNameConstants.PAGE + searchDate, Map.class);
        List<Map> maps = results.getMappedResults();
        if (maps.size() == 0) return 0;
        Object count = maps.get(0).get("count");
        return Integer.parseInt(count.toString());
    }

    /**
     * 保留
     *
     * @param searchDate
     * @param platform
     * @param channel
     * @param moneyProperty
     * @return
     */
    public BigDecimal getMoney(String searchDate, String platform, String channel, String moneyProperty) {
        Criteria criteria = this.commonCriteria(platform, channel, searchDate, null);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("isPay").last("isPay").as("isPay").sum(moneyProperty).as("sumNumber")
        );
        AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, MongoCollectionNameConstants.ORDER + searchDate, Map.class);
        List<Map> maps = results.getMappedResults();
        if (maps.size() == 0) return BigDecimal.ZERO;
        for (Map map : maps) {
            if (map.get("isPay").toString().equals("1")) {
                return new BigDecimal(map.get("sumNumber").toString());
            }
        }
        return BigDecimal.ZERO;
    }

    /**
     * 查询时间格式化
     *
     * @param dataStatisticsRequest
     * @return
     */
    public void getSearchDate(DataStatisticsRequest dataStatisticsRequest) {
        LocalDate day = LocalDate.parse(dataStatisticsRequest.getSearchDate());
        LocalDateTime startTime = day.atStartOfDay();
        LocalDateTime endTime = startTime.plusDays(1);
        dataStatisticsRequest.setStartTime(startTime);
        dataStatisticsRequest.setEndTime(endTime);
    }

    /**
     * 获取渠道并且获取渠道的子渠道
     */
    public List<String> getChannelList(String channel) {
        List<String> channelList = new ArrayList<>();
        channelList.add(channel);
        List<String> strings = dataStatisticDao.childListByChannel(channel);
        if (null != strings) channelList.addAll(strings);
        return channelList;
    }

    /**
     * 通用查询 条件拼接
     */
    public Criteria commonCriteria(String platform, String channel, String searchDate, String pathCode) {
        Criteria criteria = Criteria.where("dayTime").is(searchDate);
        if (StringUtils.isNotEmpty(channel)) criteria.and("channel").is(channel);
        if (StringUtils.isNotEmpty(platform)) criteria.and("platform").is(platform);
        if (StringUtils.isNotEmpty(pathCode)) criteria.and("pathCode").is(pathCode);
        return criteria;
    }


}
